#include <inttypes.h>
#include <p32xxxx.h>
#include <plib.h>

#include "sync.h"
#include "shifter.h"
#include "frame.h"

SyncDetector Sync;

uint32_t SyncDetector::CTInterruptCount = 0;
uint32_t SyncDetector::FrameCount = 0;
uint32_t SyncDetector::LineCount = 0;
uint32_t SyncDetector::LineNumber = 0;
uint32_t SyncDetector::LineState = 0;
uint32_t SyncDetector::LastFrameLineCount = 0;
uint32_t SyncDetector::DMAInterrupts = 0;

uint32_t SyncDetector::StartOfPixelsTime 
#ifdef USE_DMA
                                        = 391;
#else
                                        = 449;
#endif

// hsync pulse: 5us
// vsync: hell
// 1 line = 1/15625th of a second, 64us
// 64e-6/(1/72e6)/2 = 2304
// from the \ front of hsync pulse to active video:
//      15us = 540 core timer clocks
//      3.5us = 126
//      5.5us = 198
//      42.6us (512 pixels) = 1533.6
//
// 64us = 4608 clock cycles at 72MHz
//      = 2304 core timer counts 
//
// on start of sync pulse: 
//      reset timer to 0
//      set interrupt to start of video
//  on end of sync pulse:
//      if timer > 3.5us && timer < 5.5us -> short sync
//      if timer > 5.5us                  -> long sync
//      
//      short sync: -> start spi/dma on timer interrupt
//      long sync:  -> line_number = 0, continue

#define SHORTSYNC_LENGTH_MAX    198

void SyncDetector::init() 
{
    // Set up Comparator Voltage Reference
    // CVRCONbits.VREFSEL = 0;     // CVref is generated by resistor network (always on 795mx512l)
    CVRCONbits.CVRR = 1;        // 0 to 0.67 CVrsrc with CVrsrc/24 step size
    CVRCONbits.CVR = 2;         // 2:0.28V, 3: 0.41V, 4: 0.55V, 5: 0.69V, 6: 0.83V
    CVRCONbits.ON = 1;

    // Set up Comparator1: feed - from CN1-, + from CVR
    CM1CONbits.EVPOL = 3;       // interrupt on: 1 = low-to-high, 10 = high-to-low, 11 = any transition
    CM1CONbits.CREF = 1;        // + input is connected to the internal CVref
    CM1CONbits.CCH = 0;         // - connected to C1IN- pin (AN4 on MAX32)
    CM1CONbits.CPOL = 1;        // 1 = invert output
    CM1CONbits.ON = 1;

    //    INTEnable(INT_CMP1, INT_ENABLED);
    CMP1ConfigInt(CMP_INT_ENABLE |
                  CMP_INT_SUB_PRI_0 |
                  CMP_INT_PRIOR_1);

    // core timer
    mConfigIntCoreTimer((CT_INT_ON | CT_INT_PRIOR_2 | CT_INT_SUB_PRIOR_0));

    LineNumber = 0;
}

void SyncDetector::shutdown() 
{
    CMP1ConfigInt(0);
    mConfigIntCoreTimer(0);
}

void SyncDetector::suspend() 
{
    CMP1ConfigInt(0);
    mConfigIntCoreTimer(0);
}

void SyncDetector::resume() 
{
    init();
}


extern "C" {
    void __ISR(_COMPARATOR_1_VECTOR, ipl1) IntComparator1Handler(void)
    {
        static uint32_t vsync = 0;
        uint32_t timerval = ReadCoreTimer();

        switch (CM1CONbits.COUT) {
        case 0:
            // start of HSYNC pulse or a continuation of VSYNC
            Shifter.abort();        // abort current transfer if it's still in progress 
            OpenCoreTimer(SyncDetector::StartOfPixelsTime/*450*/);
            mConfigIntCoreTimer((CT_INT_ON | CT_INT_PRIOR_2 | CT_INT_SUB_PRIOR_0));
            break;
        case 1:
            // end of sync pulse
            if (timerval < SHORTSYNC_LENGTH_MAX) {
                // short sync
                // the interrupt will fire and initiate the acquisition
                SyncDetector::LineState = 0;
                if (vsync) {
                    vsync = 0;
                    SyncDetector::FrameCount++;
                }
            } 
            else {
                // long sync
                WriteCoreTimer(0);  // recock, the interrupt will not fire
                if (vsync == 0) {
                    SyncDetector::LastFrameLineCount = SyncDetector::LineNumber;
                }
                SyncDetector::LineNumber = 0;
                FrameBuffer.NewFrame();
                vsync = 1;
            }
            break;
        }

        mCMP1ClearIntFlag();
    }

#ifndef _ENABLE_CORE_TIMER_IN_WIRING
    void __ISR(_CORE_TIMER_VECTOR, ipl2) IntCoreTimerHandler(void)
    {
#ifndef USE_DMA
        INTDisableInterrupts();
#endif
        mCTClearIntFlag();
        // initiate the transfer somehow...

        SyncDetector::CTInterruptCount++;

        switch (SyncDetector::LineState) {
        case 0:
            if (CM1CONbits.COUT) {
                // this is the beginning of a video line after HSYNC pulse

                // update timer to fire after the end of line
                // this value gets added to current compare register
                // not needed in non-dma mode
                //UpdateCoreTimer(1541);  

                // initiate line acquisition, but
                // only really acquire even frames
                // on odd frames leave time for USB transmission
                FrameBuffer.NewLine();
                Shifter.start(FrameBuffer.GetCurrentLinePtr(), 
                              Frame::LineSizeInBytes); 

                SyncDetector::LineState++;
                SyncDetector::LineCount++;
                SyncDetector::LineNumber++;
            }
            break;
        case 1:
            SPI2CONbits.ON = 0;     // enough, stop it
            SyncDetector::LineState++;
            break;
        }
#ifndef USE_DMA
        INTEnableInterrupts();
#endif
    }
#endif

    void __ISR(_DMA_1_VECTOR, ipl1) Dma1Vector(void) {
        SPI1CONbits.ON = 0;
        SPI2CONbits.ON = 0;
        mDmaChnClrIntFlag(1);
        DmaChnClrEvFlags(DMA_CHANNEL1, DMA_EV_ALL_EVNTS);
        SyncDetector::DMAInterrupts++;
    }
}
